﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.ComponentModel;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Security.Permissions;

namespace ESpace
{
    
    // класс лога для tcp
    class TcpLog
    {
        int MaxSizeLog = 100;
        List<String> Log = new List<string>();
        public Control MainCtrl;
        public TcpLog(Control MainCtrl)
        {
            this.MainCtrl = MainCtrl;
        }
        public TcpLog(Control MainCtrl, int MaxSizeLog)
        {
            this.MainCtrl = MainCtrl;
            this.MaxSizeLog = MaxSizeLog;
        }

        public void WriteLog(string s)
        {
            if (MainCtrl.InvokeRequired)
            {
                MainCtrl.Invoke(new AddLogDelegate(AddLog), s);
            }
            else
            {
                AddLog(s);
            }
        }

        delegate void AddLogDelegate(string s);
        void AddLog(string s)
        {
            DateTime dt = DateTime.Now;
            String s1 = dt.ToString() + "." + dt.Millisecond.ToString("000") + " *** " + s;
            Log.Add(s1);
            // убираем первые записи в случае превышения максимального размера лога
            while (Log.Count >= MaxSizeLog)
                Log.RemoveAt(0);
        }

        public void ClearLog()
        {
            Log.Clear();
        }

        public String[] GetLog()
        {
            return Log.ToArray();
        }

    }

    public class ETcp
    {
        /// <summary>
        /// преобразование IPAddress в строку типа ххх.ххх.ххх.ххх
        /// </summary>
        static public String GetIpStr(IPAddress Addr)
        {
            byte[] b = Addr.GetAddressBytes();
            if (b.Length == 0) return "";
            String s = b[0].ToString();
            for (int i = 1; i < b.Length; i++)
                s += "." + b[i].ToString();
            return s;
        }

        /// <summary>
        /// преобразование строки типа ххх.ххх.ххх.ххх в IPAddress
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        static public IPAddress GetAddr(String s)
        {
            String[] sL = s.Split('.');
            if (sL.Length != 4 & sL.Length != 6) return null;
            byte[] b = new byte[sL.Length];
            for (int i = 0; i < b.Length; i++)
                if (!byte.TryParse(sL[i], out b[i])) return null;
            return new IPAddress(b);
        }
    }

    public class EGif
    {
        Image img;
        double cntTime = 0;
        int actF = 0;
        Frame[] frm;

        public bool b = false; // для EChatWindow

        public int Width { get { return img.Width; } }
        public int Height { get { return img.Height; } }

        public Bitmap ActBmp { get { return frm[actF].bmp; } }

        class Frame
        {
            public Bitmap bmp;
            public int delay;
        }
        
        public EGif(Image gifImage)
        {
            img = gifImage;
            SplitGif();
        }

        // разложить gif по кадрам
        void SplitGif()
        {
            int FrameCount = img.GetFrameCount(FrameDimension.Time);
            frm = new Frame[FrameCount];
            byte[] b = img.GetPropertyItem(0x5100).Value;
            for (int i = 0; i < FrameCount; i++)
            {
                frm[i] = new Frame();
                frm[i].delay = (int)BitConverter.ToUInt32(b, i * 4) * 10;
            }
            PropertyItem pp = img.GetPropertyItem(0x5100);
            frm[0].bmp = new Bitmap(img);

            for (int i = 1; i < FrameCount; i++)
            {
                img.SelectActiveFrame(FrameDimension.Time, i);
                frm[i].bmp = new Bitmap(img);
            }
        }

        public bool Run(double elTime)
        {
            cntTime += elTime;
            bool ret = false;
            while (cntTime >= frm[actF].delay)
            {
                cntTime -= frm[actF].delay;
                actF++;
                if (actF >= frm.Length) actF = 0;
                ret = true;
            }
            return ret;
        }
    }

    public class EGif1
    {
        Bitmap img;
        double cntTime = 0;
        int actF = 0;
        int len = 1;

        int[] delay;

        public int Width { get { return img.Width; } }
        public int Height { get { return img.Height; } }

        public Bitmap ActBmp 
        { 
            get 
            { 
                if(len == 1) return img;
                if (actF == 0) return img;
                img.SelectActiveFrame(FrameDimension.Time, actF);
                return new Bitmap(img);
            } 
        }

        public EGif1(Bitmap gifImage)
        {
            img = gifImage;
            SplitGif();
        }

        // разложить gif по кадрам
        void SplitGif()
        {
            len = img.GetFrameCount(FrameDimension.Time);
            delay = new int[len];
            byte[] b = img.GetPropertyItem(0x5100).Value;
            for (int i = 0; i < len; i++)
            {
                delay[i] = (int)BitConverter.ToUInt32(b, i * 4) * 10;
            }
        }

        public bool Run(double elTime)
        {
            if (len==1) return false;
            cntTime += elTime;
            bool ret = false;
            while (cntTime >= delay[actF])
            {
                cntTime -= delay[actF];
                actF++;
                if (actF >= len) actF = 0;
                ret = true;
            }
            return ret;
        }
    }

    public class TextStlitter
    {
        public class StrItem
        {
            public int x;
            public int w;
            public String s;
            public bool tag = false;
            public int shH = 0; // смещение от положения Y всей строки для тега
            public StrItem(int X, int W, String S, bool bTag, int ShH)
            {
                x = X;
                s = S;
                w = W;
                tag = bTag;
                shH = ShH;
            }
        }

        public class StringList
        {
            public List<StrItem> strL = new List<StrItem>();
            public int y;
            public int shH = 0; // максимальное смещение от положения Y для тегов
            public int h = 0;
            public StringList(int Y, int H)
            {
                y = Y;
                h = H;
            }

        }

        public delegate Size DelegateGetTagSize(String tag);

        public static void SplText(List<StringList> sl, String s, int Width, Font fnt, DelegateGetTagSize GetTagSize)
        {
            // MeasureText измеряет строку по ширине + пробел с каждой стороны и по высоте всегда одинакого при одном шрифте
            Size shSz = TextRenderer.MeasureText(" ", fnt); // измеряем пробел
            int sh = shSz.Width / 3; // смещение по х из за погрешности измерения (+1 пробел с каждой стороны)
            int H = shSz.Height; // высота строки текста. При неизменном размере шрифта всегда одинакова
            Size tagSz = new Size(-1, -1); // размер тега
            int maxW = Width; // максимальная шинина строки
            String s1 = ""; // для добавления подстроки
            int x = 0; // положение по x для добавления следующго итема
            // если лист строк пуст, добавляем первую строку
            if (sl.Count == 0) sl.Add(new StringList(0, H));
            int ind = sl.Count - 1; // текущий номер строки в которую будет добавлен очередной итем
            bool bEnter = false; // ентер
            // если в строке уже есть итемы
            if (sl[ind].strL.Count > 0)
            {
                // берем последний итем
                StrItem tmpS = sl[ind].strL[sl[ind].strL.Count - 1];
                // смотрим позицию вставки следующего итема 
                x = tmpS.x + tmpS.w;
            }
            int w = 0; // ширина текущего итема для добавления

            // пробегаемся по символам текста
            for (int i = 0; i < s.Length; i++)
            {
                // смотрим тег
                bool bTag = false;
                String s2 = "";
                // если текущий символ - это < и длина достаточная для минимального тега
                if (s[i] == '<' && i < (s.Length - 2))
                {
                    // пробегаемя по строке
                    for (int j = (i + 1); j < s.Length; j++)
                    {
                        // если найден закрывающий символ
                        if (s[j] == '>')
                        {
                            // формируем строку тега
                            s2 = s.Substring(i, j - i + 1);

                            // проверяем корректность строки тега
                            tagSz = GetTagSize(s2);
                            if (tagSz.Width != -1)
                                bTag = true;
                            break;
                        }
                    }
                }
                // если тег был учпешно найден
                if (bTag)
                {
                    // если до тега есть подстрока, добавляем ее в текущую строку
                    if (s1.Length != 0)
                    {
                        // измеряем подстроку
                        Size sz = TextRenderer.MeasureText(s1, fnt);
                        // запоминаем ширину подстроки
                        w = sz.Width - sh * 2;

                        // добавляем подстроку без текущего символа в итем текущей строки
                        sl[ind].strL.Add(new StrItem(x, w, s1, false, 0));
                        // очищаем подстроку
                        s1 = "";
                        // устанавливаем положение по х для добавления следующего итема
                        x = x + w;
                        // устанавливаем ширину следующего итема в 0
                        w = 0;
                    }

                    // присваиваем ширину и высоту тега (находжение размеров выше. при проверки правильности тега)
                    int tagW = tagSz.Width;
                    int tagH = tagSz.Height;
                    // если ширина тега + положение по х больше максимума, то переносим тег на другую строку
                    if ((x + tagW) > maxW)
                    {
                        // добавляем новую пустую строку
                        int y = sl[ind].y + tagH;
                        sl.Add(new StringList(y, H));
                        ind++;
                        // смотрим не изменилась ли высота строки из-за тега
                        int shH = tagH - H; if (shH < 0) shH = 0;
                        if (sl[ind].shH < shH) sl[ind].shH = shH;
                        // устанавливаем положение по х для добавления следующего итема в начало
                        x = 0;
                        // устанавливаем ширину следующего итема в 0
                        w = 0;
                        // добавляем в строку тег
                        sl[ind].strL.Add(new StrItem(x, tagW, s2, true, shH));
                        // устанавливаем положение следующего итема
                        x = x + tagW;
                    }
                    // иначе добавляем тег в текущую строку
                    else
                    {
                        // смотрим не изменилась ли высота строки из-за тега
                        int shH = tagH - H; if (shH < 0) shH = 0;
                        if (sl[ind].shH < shH)
                        {
                            int diff = shH - sl[ind].shH;
                            sl[ind].shH += diff;
                            // смещаем строку по y
                            sl[ind].y += diff;
                        }

                        // добавляем в строку тег
                        sl[ind].strL.Add(new StrItem(x, tagW, s2, true, shH));
                        // устанавливаем положение по х для добавления следующего итема
                        x = x + tagW;
                        // устанавливаем ширину следующего итема в 0
                        w = 0;
                    }
                    // перескакиваем через подстроку с тегом
                    i += s2.Length - 1;
                }
                // если тега нет
                else
                {
                    // смотрим не ентер ли это
                    bEnter = false;
                    if (i < (s.Length - 1))
                        if (s[i] == '\r' && s[i + 1] == '\n')
                            bEnter = true;

                    // если не ентер измеряем подстроку, учитывая текущий символ
                    if (!bEnter)
                    {
                        // измеряем подстроку, учитывая текущий символ
                        Size sz = TextRenderer.MeasureText(s1 + s[i], fnt);
                        // запоминаем ширину подстроки, учитывая текущий символ
                        w = sz.Width - 2 * sh;
                    }

                    // если пложение по х + ширина подстроки превысила максимальную ширину или энтер
                    if ((w + x) > maxW || bEnter)
                    {
                        // если подстрока есть
                        // добавляем подстроку без текущего символа в новый итем текущей строки
                        if (s1.Length > 0) sl[ind].strL.Add(new StrItem(x, w, s1, false, 0));
                        // очищаем подстроку
                        s1 = "";
                        // добавляем новую пустую строку
                        int y = sl[ind].y + sl[ind].h; // считаем y для сделующей строки
                        sl.Add(new StringList(y, H)); // добавляем новую пустую строку
                        ind++; // меняем индекс последней строки
                        // устанавливаем положение по х для добавления следующей строки-итема
                        x = 0;
                        // устанавливаем ширину следующей строки-итема в 0
                        w = 0;
                        // если ентер, то перескакиваем через 1 cимвол
                        if (bEnter) i += 1;
                    }
                    // если это не ентер добавляем к подстроке очередной символ
                    if (s[i] != '\n') s1 += s[i];
                }
            }

            // окончательое добавление
            // если подстрока не пуста, добавляем ее в текущую строку.
            if (s1.Length > 0)
            {
                w = TextRenderer.MeasureText(s1, fnt).Width - 2 * sh; // смотрим ширину
                sl[ind].strL.Add(new StrItem(x, w, s1, false, 0)); // добавляем
            }
            // Если последняя строка пуста, удаляем ее
            if (sl[ind].strL.Count == 0 && !bEnter) sl.RemoveAt(ind);

        }

    }

    class Utility
    {
        #region Timer Internal Stuff

        // Функция   QueryPerformanceFrequency   извлекает частоту счетчика производительности, если таковой существует. 
        [System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously
        [DllImport("kernel32")]
        static extern bool QueryPerformanceFrequency(ref long PerformanceFrequency);

        //Функция   QueryPerformanceCounter   извлекает текущее значение счетчика производительности, если таковой существует. 
        [System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously
        [DllImport("kernel32")]
        static extern bool QueryPerformanceCounter(ref long PerformanceCount);

        static bool isTimerInitialized = false;
        static long lTicksPerSec = 0; // тиков в секунду
        static long lLastElapsedTime = 0; // последнее время выхова фии
        #endregion

        /// <summary>
        /// возвращает пройденное время в секундах с прошлого вызова (или fps)
        /// </summary>
        public static float GetElapsedTime()
        {
            // извлекаем текущее значение счетчика производительности
            long ActTime = 0;
            QueryPerformanceCounter(ref ActTime);

            // при первом вызове таймера
            if (!isTimerInitialized)
            {
                // извлекаем частоту счетчика производительности
                lTicksPerSec = 0;
                QueryPerformanceFrequency(ref lTicksPerSec);

                lLastElapsedTime = ActTime;

                // таймер инициализирован
                isTimerInitialized = true;
            }

            // возвращаем пройденное время в секундах
            double ret = (double)(ActTime - lLastElapsedTime) / (double)lTicksPerSec;
            lLastElapsedTime = ActTime; // запоминаем последнее значение
            return (float)ret;
        }
    }

}
